from struct import unpack
from http.server import HTTPServer, BaseHTTPRequestHandler
from http.client import HTTPResponse
from socketserver import ThreadingMixIn
import socket
import time, random, select


x_sogou_auth = "9CD445F1E7ADB0BD803C22AD1D545F80/64/453edc6d49ba4e27"
proxy_host = 'h' + str(random.randint(0,10)) + '.edu.bj.ie.sogou.com'
proxy_port = 80
BufferSize = 8192
RemoteTimeout = 15

def calTag(t, host):
    s = (t + host + 'SogouExplorerProxy').encode('utf8')
    code = len(s)
    dwords = code >> 2
    rest = code % 4
    v = unpack(str(dwords) + 'i' + str(rest) + 's', s)
    for vv in v:
        if(type(vv) == type(b'')):
            break
        a = (vv & 0xFFFF)
        b = (vv >> 16)
        code += a
        code = code ^ (((code << 5) ^ b) << 11)
        # To avoid overflows
        code &= 0xffffffff
        code += code >> 11
    if rest == 3:
        code += s[len(s)-2] * 256 + s[len(s)-3]
        code = code ^ ((code ^ (s[len(s)-1]) * 4) << 0x10)
        code &= 0xffffffff
        code += code >> 11
    elif rest == 2:
        code += s[len(s)-1] * 256 + s[len(s)-2]
        code ^= code << 11
        code &= 0xffffffff
        code += code >> 17
    elif rest == 1:
        code += s[len(s)-1]
        code ^= code << 10
        code &= 0xffffffff
        code += code >> 1
    code ^= code * 8
    code &= 0xffffffff
    code += code >> 5
    code ^= code << 4
    code = code & 0xffffffff
    code += code >> 17
    code ^= code << 25
    code = code & 0xffffffff
    code += code >> 6
    code = code & 0xffffffff
    return hex(code)[2:].rstrip('L').zfill(8)

class Handler(BaseHTTPRequestHandler):
    remote = None

    # Ignore Connection Failure
    def handle(self):
        try:
            BaseHTTPRequestHandler.handle(self)
        except socket.error: pass
    def finish(self):
        try:
            BaseHTTPRequestHandler.finish(self)
        except socket.error: pass

    # CONNECT Data Transfer
    def transfer(self, a, b):
        fdset = [a, b]
        while True:
            r,w,e = select.select(fdset, [], [])
            if a in r:
                data = a.recv(BufferSize)
                if not data: break
                b.sendall(data)
            if b in r:
                data = b.recv(BufferSize)
                if not data: break
                a.sendall(data)

    def sogouProxy(self):
        try:
            if self.remote is None or self.lastHost != self.headers["Host"]:
                self.remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self.remote.settimeout(RemoteTimeout)
                self.remote.connect((proxy_host, proxy_port))

            self.remote.sendall(self.requestline.encode('utf8') + b"\r\n")
            t = hex(int(time.time()))[2:].rstrip('L').zfill(8)
            self.headers["X-Sogou-Timestamp"] = t
            self.headers["X-Sogou-Tag"] = calTag(t, self.headers['Host'])
            self.headers["X-Sogou-Auth"] = x_sogou_auth
            headerstr = str(self.headers).replace("\r\n", "\n").replace("\n", "\r\n")
            self.remote.sendall(headerstr.encode('utf8'))

            if self.command == 'POST':
                self.remote.sendall(self.rfile.read(int(self.headers['Content-Length'])))

            response = HTTPResponse(self.remote, method=self.command)
            response.begin()
            version = 'HTTP/1.0' if(response.version == 10) else 'HTTP/1.1'
            status = version + ' ' + str(response.status) + " " + response.reason
            self.wfile.write(status.encode('utf8') + b'\r\n')

            h = ''
            for hh, vv in response.getheaders():
                if hh.upper() != 'TRANSFER-ENCODING':
                    h += hh + ': ' + vv + '\r\n'
            self.wfile.write(h.encode('utf8') + b'\r\n')

            if self.command == "CONNECT" and response.status == 200:
                return self.transfer(self.remote, self.connection)
            else:
                while True:
                    response_data = response.read(BufferSize)
                    if not response_data: break
                    self.wfile.write(response_data)

        except socket.error:
            pass
    do_POST = do_GET = do_CONNECT = sogouProxy

class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
    pass

print('Proxy over ' + proxy_host)
print('Please set proxy server to 127.0.0.1:3000\n')
server = ThreadingHTTPServer(("127.0.0.1", 3000), Handler)
server.serve_forever()
